package cn.lg.soar.flow.biz.service.impl;

import cn.lg.soar.flow.api.model.ApproverDTO;
import cn.lg.soar.flow.api.model.AuditLogDTO;
import cn.lg.soar.flow.api.utils.ProcessUtils;
import cn.lg.soar.flow.biz.entity.SoarAuditLog;
import cn.lg.soar.flow.biz.mapper.SoarAuditLogMapper;
import cn.lg.soar.flow.biz.service.SoarAuditLogService;
import cn.lg.soar.system.api.model.UserQueryDTO;
import cn.lg.soar.system.api.service.IUserApi;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 节点审批人
 * @author luguoxiang
 * @since 2025-08-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Service
public class SoarAuditLogServiceImpl extends ServiceImpl<SoarAuditLogMapper, SoarAuditLog> implements SoarAuditLogService {

    @Autowired
    private IUserApi userApi;

    @Override
    public List<AuditLogDTO> list(String procInstId) {
        List<AuditLogDTO> list = baseMapper.listByProcInstId(procInstId);
        setUserName(list);
        return list;
    }

    @Override
    public List<AuditLogDTO> list(String flowCode, String bizId) {
        List<AuditLogDTO> list = baseMapper.listByBiz(flowCode + ":%", bizId);
        setUserName(list);
        return list;
    }

    private void setUserName(List<AuditLogDTO> list) {
        if (list.isEmpty()) {
            return;
        }
        Set<Long> userIds = new HashSet<>(list.size());
        List<AuditLogDTO> tasks = new ArrayList<>(list.size());
        for (AuditLogDTO x : list) {
            if (x.getEndTime() == null) {
                tasks.add(x);
            } else if (x.getUserId() != null) {
                userIds.add(x.getUserId());
            }
        }
        Map<String, Set<Long>> taskMap = new HashMap<>(tasks.size());
        if (!tasks.isEmpty()) {
            List<String> taskIds = tasks.stream().map(AuditLogDTO::getTaskId).toList();
            List<Map<String, String>> candidates = baseMapper.getCandidates("'" + String.join("','", taskIds) + "'");
            Map<String, List<Map<String, String>>> taskGroup = candidates.stream().collect(Collectors.groupingBy(x -> x.get("TASK_ID_")));
            taskGroup.forEach((k, v) -> {
                Set<Long> taskUserIds = new HashSet<>(v.size());
                Set<Long> taskPostIds = new HashSet<>(v.size());
                for (Map<String, String> x : v) {
                    String candidate = x.get("USER_ID_");
                    if (ProcessUtils.isPost(candidate)) {
                        taskPostIds.add(ProcessUtils.toPostId(candidate));
                    } else {
                        taskUserIds.add(ProcessUtils.toUserId(candidate));
                    }
                }
                if (!taskPostIds.isEmpty()) {
                    taskUserIds.addAll(userApi.idsByPostId(taskPostIds));
                }
                taskMap.put(k, taskUserIds);
                userIds.addAll(taskUserIds);
            });
        }
        Map<Long, UserQueryDTO> userMap = userApi.mapByIds(userIds);
        UserQueryDTO userQueryDTO = new UserQueryDTO();
        userQueryDTO.setId(0L);
        userQueryDTO.setUsername(ProcessUtils.SYSTEM_APPROVAL);
        userQueryDTO.setNickname("系统");
        userMap.put(userQueryDTO.getId(), userQueryDTO);
        for (AuditLogDTO x : list) {
            Long userId = x.getUserId();
            if (userId != null) {
                UserQueryDTO user = userMap.get(userId);
                x.setApprovers(List.of(new ApproverDTO(user.getUsername(), user.getNickname())));
            } else {
                Set<Long> ids = taskMap.get(x.getTaskId());
                if (ids != null) {
                    List<ApproverDTO> approvers = ids.stream()
                            .map(uid -> userMap.computeIfAbsent(uid, k -> {
                                UserQueryDTO user = new UserQueryDTO();
                                user.setUsername(uid.toString());
                                return user;
                            }))
                            .map(user -> new ApproverDTO(user.getUsername(), user.getNickname()))
                            .toList();
                    x.setApprovers(approvers);
                }
            }
        }
    }

}
